#include "Application.hpp"
#include "Control.hpp"
#include <map>
using namespace NAMESPACENAME;
std::map<HWND, Control*> __mapWindows;
HINSTANCE Application::theApp;
ConfigFile Application::configFile;
int Application::MainLoop()
{
	MSG msg;
	while (GetMessageW(&msg, nullptr, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return (int)msg.wParam;
}
int Application::MainLoop(void(*fn)())
{
	MSG msg;
	while (true)
	{
		if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT)
				break;
			TranslateMessage(&msg);
			DispatchMessageW(&msg);
		}
		else
			fn();
	}
	return (int)msg.wParam;
}
bool Application::Initialize(HINSTANCE hInstance)
{
	if (!ConfigFile::configFile.Read())
		return false;
	Application::theApp = hInstance; // Store instance handle in our global variable
	WNDCLASSEXW wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);

	wcex.style = CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc = Application::WindowProc;
	wcex.cbClsExtra = 0;
	wcex.cbWndExtra = 0;
	wcex.hInstance = hInstance;
	wcex.hIcon = LoadIconW(nullptr, IDI_APPLICATION);
	wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
	wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wcex.lpszMenuName = nullptr;
	wcex.lpszClassName = L"MainWindow";
	wcex.hIconSm = LoadIconW(nullptr, IDI_APPLICATION);
	if (!RegisterClassExW(&wcex))
		return false;
	wcex.style = 0;
	wcex.lpszClassName = L"ChildWindow";
	return RegisterClassExW(&wcex);
}
LRESULT __stdcall Application::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (auto win(__mapWindows.find(hWnd)); win != __mapWindows.end())
	{
		switch (message)
		{
		case WM_MOUSEMOVE:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnMouseMove(mouseEventArgs);
		}
		break;
		case WM_LBUTTONDOWN:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnLeftButtonDown(mouseEventArgs);
		}
		break;
		case WM_LBUTTONUP:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnLeftButtonUp(mouseEventArgs);
		}
		break;
		case WM_RBUTTONDOWN:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnRightButtonDown(mouseEventArgs);
		}
		break;
		case WM_RBUTTONUP:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnRightButtonUp(mouseEventArgs);
		}
		break;
		case WM_MOUSEHOVER:
		{
			MouseEventArgs mouseEventArgs(POINT{ GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam) }, *reinterpret_cast<KeyStates*>(&wParam));
			win->second->OnMouseHover(mouseEventArgs);
		}
		break;
		case WM_COMMAND:
			if (auto c(__mapWindows.find(reinterpret_cast<HWND>(lParam))); c != __mapWindows.end())
				win->second->OnCommand(HIWORD(wParam), LOWORD(wParam), dynamic_cast<Control*>(c->second));
			else
				win->second->OnCommand(HIWORD(wParam), LOWORD(wParam), nullptr);
			break;
		case WM_CLOSE:
			win->second->OnClose();
			break;
		case WM_DESTROY:
			win->second->OnDestroy();
			break;
		case WM_NCPAINT:
			win->second->OnNcPaint((HRGN)wParam);
			break;
		case WM_SIZING:
		{
			Rectangle rect(*reinterpret_cast<RECT*>(lParam));
			SizingEventArgs sea;
			sea.rect = &rect;
			sea.sizingType = (SizingEventArgs::SizingType)wParam;
			win->second->OnSizing(sea);
			*reinterpret_cast<RECT*>(lParam) = rect;
		}
		return true;
		case WM_VSCROLL:
		{
			ScrollEventArgs e;
			e.scrollType = (ScrollEventArgs::ScrollType)LOWORD(wParam);
			e.position = HIWORD(wParam);
			win->second->OnVerticalScroll(e);
		}
		break;
		case WM_SIZE:
		{
			SizeEventArgs sea;
			sea.newHeight = HIWORD(lParam);
			sea.newWidth = LOWORD(lParam);
			sea.sizeType = (SizeEventArgs::SizeType)wParam;
			win->second->OnSize(sea);
		}
		break;
		default:
			return DefWindowProcW(hWnd, message, wParam, lParam);
		}
		return 0;
	}
	return DefWindowProcW(hWnd, message, wParam, lParam);
}